package edu.metrostate.capstonediner;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

public abstract class SpriteBuffer {
	// Size in bytes of a short primitive
	private static final int SHORT_SIZE = 2;
	// Size in bytes of a float primitive
	private static final int FLOAT_SIZE = 4;
	// Number of coordinates vertex
	private static final int VERTEX_COORDINATES = 2;
	// Number of texture coordinates per vertex
	private static final int TEXTURE_COORDINATES = 2;
	// Number of vertices per sprite
	protected static final int VERTICES_PER_SPRITE = 4;
	// Number of indices per sprite
	protected static final int INDICES_PER_SPRITE = 6;
	// Default maximum number of sprites 
	private static final int DEFAULT_MAX_SPRITES = 10;	
	
	// contains the vertices to be stored in buffer
	protected float[] verticesList;
	// contains the indices to be stored in buffer
	protected short[] indicesList;
	// Contains the vertices to be drawn
	private FloatBuffer verticesBuffer;
	// Contains the indices to specify the vertices to be used
	private ShortBuffer indicesBuffer;
	// Stores the maximum number of sprites that the buffers can currently hold
	protected int maxSprites;
	// Stores the number of sprites
	protected int size = 0;
	// Used to store the texture for sprites
	protected GameTexture texture;
	
	// Used to Synchronize changes to the data list
	protected final Object listLock = new Object();
	// Used to Synchronize changes to the buffers
	protected final Object bufferLock = new Object();
	
	public SpriteBuffer() {
		// Initialize vertices
		createBuffers(DEFAULT_MAX_SPRITES);
	}
	
	public void onDrawFrame(GL10 gl) {
		synchronized(bufferLock) {
			synchronized(listLock) {
				// Copies the data lists to buffers
				verticesBuffer.clear();
				verticesBuffer.put(verticesList);
				verticesBuffer.flip();
				
				indicesBuffer.clear();
				indicesBuffer.put(indicesList);
				indicesBuffer.flip();
			}
			
			// specifies texture
			texture.bind();
			
			// specifies vertices
			verticesBuffer.position(0);
			gl.glVertexPointer(VERTEX_COORDINATES, GL10.GL_FLOAT, (VERTEX_COORDINATES + TEXTURE_COORDINATES) * FLOAT_SIZE, verticesBuffer);
			verticesBuffer.position(VERTEX_COORDINATES);
			gl.glTexCoordPointer(VERTEX_COORDINATES, GL10.GL_FLOAT, (VERTEX_COORDINATES + TEXTURE_COORDINATES) * FLOAT_SIZE, verticesBuffer);
			
			// draws sprites
			gl.glDrawElements(GL10.GL_TRIANGLES, size * INDICES_PER_SPRITE, GL10.GL_UNSIGNED_SHORT, indicesBuffer);
		}
	}
	
	/**
	 * Creates buffer objects.
	 * 
	 * @param maxSprites Specifies the maximum number of sprites the buffers can contain.
	 */
	protected void createBuffers(int maxSprites) {
		synchronized(bufferLock) {
			ByteBuffer byteBuffer = ByteBuffer.allocateDirect(maxSprites * VERTICES_PER_SPRITE * (VERTEX_COORDINATES + TEXTURE_COORDINATES) * FLOAT_SIZE);
			byteBuffer.order(ByteOrder.nativeOrder());
			verticesBuffer = byteBuffer.asFloatBuffer();
			
			byteBuffer = ByteBuffer.allocateDirect(maxSprites * INDICES_PER_SPRITE * SHORT_SIZE);
			byteBuffer.order(ByteOrder.nativeOrder());
			indicesBuffer = byteBuffer.asShortBuffer();
		}
		
		synchronized(listLock) {
			verticesList = new float[maxSprites * VERTICES_PER_SPRITE * (VERTEX_COORDINATES + TEXTURE_COORDINATES)];
			indicesList = new short[maxSprites * INDICES_PER_SPRITE];
		}
		
		this.maxSprites = maxSprites;
	}
}
